import yaml
import blue
import os
import geo2
import blue
import copy
import trinity
import math
from actorfactory import ActorFactory
from actorfactory.PropertyApplier import ApplyPropertiesToObject

class BaseNode(object):

    def __init__(self):
        object.__init__(self)
        self.persistData = ['name']
        self.excludeList = []
        self.name = ''
        self.parent = None



    def __getstate__(self):
        res = {}
        self.ExcludeList = []
        self.GetExclusionList()
        for each in self.persistData:
            if (each not in self.excludeList):
                res[each] = getattr(self, each)

        return res



    def __setstate__(self, data):
        self.__init__()
        for each in data.keys():
            setattr(self, each, data[each])




    def GetExclusionList(self):
        return 



    def Save(self, path):
        self.PreSave()
        saveFile = file(blue.rot.PathToFilename(path), 'w+')
        yaml.dump(self, saveFile, default_flow_style=False)
        saveFile.close()



    def Load(path, parent = None):
        file = open(blue.rot.PathToFilename(path))
        if (file != None):
            data = yaml.load(file)
            file.close()
            data.parent = parent
            if hasattr(data, 'PostLoad'):
                data.PostLoad()
            return data


    Load = staticmethod(Load)

    def PostLoad(self):
        return 



    def PreSave(self):
        return 



    def GatherProperties(self, props):
        if (self.parent != None):
            return self.parent.GatherProperties(props)
        else:
            return props




class ListItem(BaseNode):

    def __init__(self):
        BaseNode.__init__(self)
        self.persistData = (self.persistData + ['position',
         'rotation',
         'properties'])
        self.position = (0.0, 0.0, 0.0)
        self.rotation = (0.0, 0.0, 0.0)
        self.properties = {}
        self.inst = None
        self.matrix = geo2.MatrixIdentity()



    def GetExclusionList(self):
        BaseNode.GetExclusionList(self)
        exclude = ['name']
        if ((hasattr(self, 'position') == False) or (self.position == (0.0, 0.0, 0.0))):
            exclude = (exclude + ['position'])
        if ((hasattr(self, 'rotation') == False) or (self.rotation == (0.0, 0.0, 0.0))):
            exclude = (exclude + ['rotation'])
        if ((hasattr(self, 'properties') == False) or (len(self.properties.keys()) == 0)):
            exclude = (exclude + ['properties'])
        self.excludeList = (self.excludeList + exclude)



    def GatherProperties(self, props):
        res = copy.copy(self.properties)
        if (self.parent != None):
            res.update(self.parent.GatherProperties(props))
        return res



    def PostLoad(self):
        BaseNode.PostLoad(self)
        self.matrix = geo2.MatrixRotationQuaternion(geo2.QuaternionRotationArc(self.rotation, (0.0, 0.0, 1.0)))
        self.matrix = (self.matrix[0],
         self.matrix[1],
         self.matrix[2],
         (self.position[0],
          self.position[1],
          self.position[2],
          1.0))



    def PreSave(self):
        BaseNode.PreSave(self)
        self.rotation = (self.matrix[2][0],
         self.matrix[2][1],
         self.matrix[2][2])
        self.position = (self.matrix[3][0],
         self.matrix[3][1],
         self.matrix[3][2])



    def Construct(self, matrix = geo2.MatrixIdentity(), props = {}):
        newMatrix = geo2.MatrixMultiply(matrix, self.matrix)
        newProps = copy.copy(self.properties)
        newProps.update(props)
        return (newMatrix, newProps)




class AssetLink(ListItem):

    def __init__(self):
        ListItem.__init__(self)
        self.inst = None
        self.Obj = None



    def PostLoad(self):
        from app.Palette.PaletteTemplate import PaletteTemplate
        ListItem.PostLoad(self)
        if ('template' in self.properties):
            resPath = blue.rot.PathToFilename(self.properties['template'])
            if os.path.exists(resPath):
                self.inst = PaletteTemplate.Load(resPath)
            else:
                print 'Template missing (',
                print resPath,
                print ')'



    def __repr__(self):
        return ('AssetLink:%s' % self.properties['template'])



    def Construct(self, matrix = geo2.MatrixIdentity(), props = {}):
        (newMatrix, newProps,) = ListItem.Construct(self, matrix, props)
        rot = geo2.QuaternionRotationGetYawPitchRoll(geo2.QuaternionRotationMatrix(newMatrix))
        vPos = geo2.Vector(newMatrix[3][0], newMatrix[3][1], newMatrix[3][2])
        vForward = geo2.Vector(self.matrix[2][0], self.matrix[2][1], self.matrix[2][2])
        if (self.inst == None):
            print 'Error: Zone unable to load template for ',
            print self.properties['template']
            return 
        self.obj = ActorFactory.CreateObject(self.inst, vPos, vForward, geo2.Vector(0.0, 1.0, 0.0), newProps)
        if (self.obj != None):
            dev = trinity.GetDevice()
            if ((dev == None) or ((dev.scene2 == None) or (dev.scene2.city == None))):
                print 'Error: no scene to construct object in '
                return 
            ActorFactory.AddToScene(dev.scene2, self.obj, self.inst)




class FileLink(ListItem):

    def __init__(self):
        ListItem.__init__(self)
        self.persistData = (self.persistData + ['filePath'])
        self.filePath = ''



    def PostLoad(self):
        ListItem.PostLoad(self)
        if (self.filePath != ''):
            filePath = blue.rot.PathToFilename(self.filePath)
            if os.path.exists(filePath):
                self.inst = FileLink.Load(filePath, self)



    def PreSave(self):
        ListItem.PreSave(self)
        if (self.inst != None):
            self.inst.Save(self.filePath)



    def __repr__(self):
        return ('FileLink:%s' % self.filePath)



    def Construct(self, matrix = geo2.MatrixIdentity(), props = {}):
        (newMatrix, newProps,) = ListItem.Construct(self, matrix, props)
        if (self.inst != None):
            self.inst.Construct(matrix, props)




class NestedLink(ListItem):

    def __init__(self):
        ListItem.__init__(self)
        self.persistData = (self.persistData + ['nestedObject'])
        self.nestedObject = None



    def PostLoad(self):
        ListItem.PostLoad(self)
        self.inst = self.nestedObject
        if self.inst:
            self.inst.parent = self
            self.inst.PostLoad()



    def PreSave(self):
        ListItem.PreSave(self)
        if self.inst:
            self.inst.PreSave()



    def __repr__(self):
        return ('Nested Link ( %s ) ' % self.inst.__repr__())



    def Construct(self, matrix = geo2.MatrixIdentity(), props = {}):
        (newMatrix, newProps,) = ListItem.Construct(self, matrix, props)
        if (self.inst != None):
            self.inst.Construct(matrix, props)




class LayerNode(BaseNode):

    def __init__(self):
        BaseNode.__init__(self)
        self.persistData = (self.persistData + ['items'])
        self.items = []



    def PostLoad(self):
        BaseNode.PostLoad(self)
        for each in self.items:
            each.parent = self
            each.PostLoad()




    def PreSave(self):
        BaseNode.PreSave(self)
        for each in self.items:
            each.PreSave()




    def __repr__(self):
        return ("Layer '%s'  Child#: %d" % (self.name, len(self.items)))



    def Construct(self, matrix = geo2.MatrixIdentity(), props = {}):
        for each in self.items:
            each.Construct(matrix, props)

        return (matrix, props)




class ZoneNode(LayerNode):

    def __init__(self):
        LayerNode.__init__(self)
        self.persistData = (self.persistData + ['properties'])



    def __repr__(self):
        return ("Zone '%s'  Child#: %d" % (self.name, len(self.items)))



    def Construct(self):
        LayerNode.Construct(self, geo2.MatrixIdentity(), self.properties)
        dev = trinity.GetDevice()
        ApplyPropertiesToObject(self.properties, dev, 'dev')
        ApplyPropertiesToObject(self.properties, dev.scene, 'scene')
        ApplyPropertiesToObject(self.properties, dev.scene2, 'scene2')




